/*
	system.cpp

	Author: Chris Haag
	Description:

		Creation and main flow of control of physics world is
		defined here.
*/

#include <stdlib.h>
#include <time.h>
#include "main.h"
#include "cube.h"
#include "stair.h"
#include "gravity.h"
#include "damping.h"
#include "timer.h"

// Don't let your programming teacher know
// that I'm using globals, but it's ok to let
// that person know I use some hungarian notation...

// Anchor to the linked list of systems (cubes, stairs,
// gravity, etc...)
CPSystem* g_pSystems = NULL;

// Matrix used for rendering shadows
GLfloat g_shadowMat[4][4];

void AddSystem(CPSystem* p)
{
	p->m_pNext = g_pSystems;
	g_pSystems = p;
}

void InitSystems()
{
	_NORMAL vnGround = { 0,1,0,0 };
	_VECTOR vLight = { -1,1,0 };
	float y = STAIRS_TOTAL_HEIGHT, z = 0;

	srand(time(NULL));

	// Add the floor
	AddSystem(new CStair(0, 0, 0, 200, 240, texFloor));

	// Add the steps
	AddSystem(new CStair(0, y, z, STAIRS_WIDTH/2.0f, STAIRS_WIDTH/2.0f));
	z -= STAIRS_WIDTH / 2.0f + STAIR_LENGTH / 2.0f;
	while (y > 0)
	{
		y -= STAIR_HEIGHT;

		if (y > 0)
			AddSystem(new CStair(0, y, z, STAIRS_WIDTH/2.0f, STAIR_LENGTH/2.0f));

		z -= STAIR_LENGTH;
	}

	AddSystem(new CGravity());
	AddSystem(new CDamping());

	// Add the cubes last
	for (int i=0; i < 12; i++)
	{
		CCube* pCube = new CCube(0,0,0,10);
		pCube->Reset();
		AddSystem(pCube);
	}

	// Generate the shadow matrix
	shadowMatrix(g_shadowMat, vnGround, vLight);
}

void FreeSystems()
{
	while (g_pSystems)
	{
		CPSystem* pNext = g_pSystems->m_pNext;
		delete g_pSystems;
		g_pSystems = pNext;
	}
}

void AnimateSystems()
{	
	CPSystem* p;
	float t;

	// tTolerance is the longest amount of time we
	// can use in a single call to Reaction, in seconds.
	// Increasing this number will increase the speed of
	// the program, but reduce the precision of each frame.
	// Setting it too high could cause dice to fall through
	// the stairs or even each other.
	static float tTolerance = 0.2f;
	
	// Get the number of seconds that have passed
	// in the simulation, NOT REAL LIFE, although,
	// there is a correspondence if you look into
	// the function.
	t = GetSimulationSecondsElapsed();
	
	while (t)
	{
		// Make every object interact with every
		// other object, depending on their respective
		// behaviors.
		p = g_pSystems;
		while (p)
		{
			p->Action(p->m_pNext);
			p = p->m_pNext;
		}

		// Change the position, orientation, and velocity
		// of each object based on what happened in
		// Action().
		p = g_pSystems;
		while (p)
		{
			p->Reaction(t < tTolerance ? t : tTolerance);
			p = p->m_pNext;
		}

		if (t < tTolerance) t = 0;
		else t -= tTolerance;
	}
}

void DrawSystems()
{
	CPSystem* p;

	// Draw everything BUT the cubes first
	// because cubes may be alpha blended
	p = g_pSystems;
	while (p)
	{
		if (p->GetType() != CPSystem::eCube)
			p->Draw();
		p = p->m_pNext;
	}

	// Draw the cubes
	p = g_pSystems;
	while (p)
	{
		if (p->GetType() == CPSystem::eCube)
			p->Draw();
		p = p->m_pNext;
	}

	// Draw the shadows
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	p = g_pSystems;

	glPushMatrix();
	glMultMatrixf((GLfloat *) g_shadowMat);
	while (p)
	{		
		p->DrawShadow(g_shadowMat);
		p = p->m_pNext;
	}
	glPopMatrix();

	glEnable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_LIGHTING);
	glEnable(GL_CULL_FACE);
}